package com.junmeng.jdemo;

import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Environment;
import android.util.Log;
import android.widget.Toast;

import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import com.junmeng.jdemo.retrofit.TestService;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.Field;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import okhttp3.OkHttpClient;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.scalars.ScalarsConverterFactory;

public class CrashHandler implements Thread.UncaughtExceptionHandler {
    private static final String TAG = "CrashHandler";

    /*系统默认的UncaughtException处理类*/
    private Thread.UncaughtExceptionHandler defaultHandler;

    private Context context;

    private static CrashHandler instance = new CrashHandler();

    /*用来存储设备信息和异常信息*/
    private Map<String, String> infos = new HashMap<String, String>();

    private CrashHandler() {
    }

    public static CrashHandler getInstance() {
        return instance;
    }


    public void init(Context context) {
        this.context = context;
        //获取系统默认的UncaughtException处理器
        this.defaultHandler = Thread.getDefaultUncaughtExceptionHandler();
        //设置该CrashHandler为程序的默认处理器
        Thread.setDefaultUncaughtExceptionHandler(this);
    }

    /**
     * 【说明】：当UncaughtException发生时会转入该方法来处理
     */
    @Override
    public void uncaughtException(Thread thread, Throwable ex) {
        if (!handleException(thread,ex) && defaultHandler != null) {
            //如果用户没有处理则让系统默认的异常处理器处理
            defaultHandler.uncaughtException(thread, ex);
        }
//        else {
//            try {
//                Thread.sleep(10000);
//
//            } catch (InterruptedException e) {
//                Log.e(TAG, "error : ", e);
//
//            } finally {//退出程序
//                android.os.Process.killProcess(android.os.Process.myPid());
//                System.exit(1);
//            }
//        }
    }

    /**
     * 【说明】：自定义错误处理（包括收集错误信息，生成错误日志文件）
     */
    private boolean handleException(Thread thread,Throwable ex) {
        if (ex == null) {
            return false;
        }
        collectDeviceInfo(context);
        saveCrashInfo2File(thread,ex);

        return true;
    }


    /**
     * 【说明】：收集应用参数信息
     */
    private void collectDeviceInfo(Context ctx) {
        try {
            PackageManager pm = ctx.getPackageManager();//获取应用包管理者对象
            PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(), PackageManager.GET_ACTIVITIES);
            if (pi != null) {
                String versionName = pi.versionName == null ? "null" : pi.versionName;
                String versionCode = pi.versionCode + "";
                String packageName = pi.packageName;
                infos.put("versionName", versionName);
                infos.put("versionCode", versionCode);
                infos.put("packageName", packageName);
                infos.put("appName", ctx.getString(R.string.app_name));
            }
        } catch (PackageManager.NameNotFoundException e) {
            Log.e(TAG, "an error occurred when collect package info...", e);
        }

        Field[] fields = Build.class.getDeclaredFields();
        for (Field field : fields) {
            try {
                field.setAccessible(true);
                infos.put(field.getName(), field.get(null).toString());
            } catch (IllegalAccessException e) {
                Log.e(TAG, "an error occurred when collect crash info...", e);
            }
        }
    }

    /**
     * 【说明】：保存错误信息到指定文件中
     */
    private String saveCrashInfo2File(Thread mainThread,Throwable ex) {
        StringBuffer sbf = new StringBuffer();
        for (Map.Entry<String, String> entry : infos.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            sbf.append(key + "=" + value + "\n");
        }

        Writer writer = new StringWriter();
        PrintWriter printWriter = new PrintWriter(writer);
        ex.printStackTrace(printWriter);

        Throwable cause = ex.getCause();
        while (cause != null) {
            cause.printStackTrace(printWriter);
            cause = cause.getCause();
        }
        printWriter.close();

        String result = writer.toString();
        sbf.append(result);
        try {
            //格式化日期，作为文件名的一部分
            DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd_HHmmss");
            String time = dateFormat.format(new Date());
            long timestamp = System.currentTimeMillis();
            String fileName = "crash-" + time + "-" + timestamp + ".log";
            if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
                File dir = FileUtil.getCrashDir(context);
                String filePath = dir.getAbsoluteFile() + File.separator + fileName;
                FileOutputStream fos = new FileOutputStream(new File(filePath));
                Log.e(TAG, "log file path:" + filePath);
                fos.write(sbf.toString().getBytes());
                fos.close();
                Toast.makeText(context, "crash日志已写入：" + filePath, Toast.LENGTH_SHORT).show();
                retrace(mainThread,sbf.toString());

            }
            return fileName;
        } catch (FileNotFoundException e) {
            Log.e(TAG, "an error occurred while find file...", e);
        } catch (IOException e) {
            Log.e(TAG, "an error occurred while writing file...", e);
        }
        return null;
    }

    private void retrace(Thread mainThread,String log) {
        X509TrustManager xtm = new X509TrustManager() {
            @Override
            public void checkServerTrusted(X509Certificate[] chain, String authType) {
            }

            @Override
            public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {

            }

            @Override
            public X509Certificate[] getAcceptedIssuers() {
                X509Certificate[] x509Certificates = new X509Certificate[0];
                return x509Certificates;
            }
        };

        SSLContext sslContext = null;
        try {
            sslContext = SSLContext.getInstance("SSL");

            sslContext.init(null, new TrustManager[]{xtm}, new SecureRandom());

        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        }
        HostnameVerifier DO_NOT_VERIFY = new HostnameVerifier() {
            @Override
            public boolean verify(String hostname, SSLSession session) {
                return true;
            }
        };
        OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .sslSocketFactory(sslContext.getSocketFactory(),xtm)
                .hostnameVerifier(DO_NOT_VERIFY)
                .build();

        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("https://192.168.0.194:18089/")
                .client(okHttpClient)
                .addConverterFactory(ScalarsConverterFactory.create())
                .build();
        TestService testService = retrofit.create(TestService.class);
       final Thread thread =new Thread(){
            @Override
            public void run() {
                try {
                    Response<String> response= testService.retrace("123",log).execute();
                    Log.e(TAG, "retrace success=" + response.body());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        };
        thread.start();
        try {
            thread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    /**
     * 【说明】：文件及文件夹工具类
     */

    public static class FileUtil {

        private static final String TAG = "FileUtil";

        /**
         * 【说明】：获取手机存储空间文件夹
         *
         * @param context 上下文
         * @return File 文件夹（/storage/emulated/0或/data/user/0/(packageName)）
         */
        private static File getAppDir(Context context) {
            String appDir = null;
            if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
                //获取外部存储路径（SD卡，在/storage/emulated/0目录下）
                appDir = Environment.getExternalStorageDirectory().getAbsolutePath();
            } else {
                //获取内部存储路径（默认在/date/user/0/(packageName)目录下）
                appDir = context.getCacheDir().getAbsolutePath();
            }
            File appFile = new File(appDir);
            if (!appFile.exists()) {
                appFile.mkdirs();
            }
            return appFile;
        }

        /**
         * 【说明】：获取应用基础文件夹（应用相关文件都会存储在该目录下）
         *
         * @param context 上下文
         * @return File 文件夹（.../AppNamexx/）
         */
        public static File getBaseDir(Context context) {
            File baseFile = new File(getAppDir(context), FileConstant.APP_BASE_DIR_NAME);
            if (!baseFile.exists()) {
                baseFile.mkdirs();
            }
            return baseFile;
        }


        /**
         * 【说明】：获取应用日志文件夹
         *
         * @param context 上下文
         * @return File 文件夹（.../AppNamexx/Log/）
         */
        public static File getLogDir(Context context) {
            File logFile = new File(getBaseDir(context), FileConstant.APP_LOG_DIR_NAME);
            if (!logFile.exists()) {
                logFile.mkdirs();
            }
            return logFile;
        }

        /**
         * 【说明】：获取应用崩溃日志文件夹
         *
         * @param context 上下文
         * @return File 文件夹（.../AppNamexx/Log/Crash/）
         */
        public static File getCrashDir(Context context) {
            File crashFile = new File(getLogDir(context), FileConstant.APP_LOG_CRASH_DIR_NAME);
            if (!crashFile.exists()) {
                crashFile.mkdirs();
            }
            return crashFile;
        }


    }

    /**
     * 【说明】：文件操作常量
     */
    public class FileConstant {

        /**
         * 应用基础文件夹名称
         */
        public static final String APP_BASE_DIR_NAME = BuildConfig.APPLICATION_ID;
        /**
         * 应用日志文件夹名称
         */
        public static final String APP_LOG_DIR_NAME = "Log";
        /**
         * 应用崩溃日志文件夹名称
         */
        public static final String APP_LOG_CRASH_DIR_NAME = "Crash";

    }


}